/**
 * jquery.rowRepeater.js
 *
 * v2.0.2
 * callback support is added
 *
 * v2.0.1
 * @author Yasar Bayar <yasar@navigatormm.com>
 */
(function ($) {
    $.fn.rowRepeater = function (options, callback) {
        options = $.extend({}, {
            data                : [],
            url                 : '',
            per_page            : 5,
            initial_page        : 1,
            enable_pagination   : true,
            template            : {
                layout          : '',
                row             : ''
            },
            beforeShow    : null,
            afterShow     : null

        }, options);

        var
        self = this,
        rows = null,
        vars = {
            total_records       : null,
            total_pages         : null,
            page                : null
        },
        $OBJ = {
            Holder: null,
            Listing: null
        },
        Load = {
            PageData: function (start, limit, callback) {
                $.getJSON(options.url, {
                    action: 'listing',
                    start: start,
                    limit: limit
                }, function (AR) {
                    if (AR.success) {
                        if(AR.data.length==0) rows[start]=null;
                        else{
                            for (var i = start, j = 0; j < limit; i++, j++) {
                                rows[i] = AR.data[j];
                            }
                        }

                        if (typeof callback === 'function') {
                            callback();
                        }
                    }
                });
            },

            SummaryData: function (callback) {
                $.getJSON(options.url, {
                    action: 'total_records'
                }, function (AR) {
                    if (AR.success) {
                        vars.total_records = AR.data;
                        vars.total_pages = Math.ceil(AR.data / options.per_page);

                        if (vars.page > vars.total_pages) {
                            vars.page = vars.total_pages;
                        }

                        if (typeof callback === 'function') {
                            callback();
                        }
                    }
                });
            }
        },
        Get = {
            PageData: function (page_nr, callback) {
                if(page_nr<1) page_nr=1;

                var
                data_arr = [],
                i, j,

                /**
                 * find the starting record number
                 */
                idx = (page_nr - 1) * options.per_page,

                /**
                 * the last page may have less records than what required
                 * so make sure that we only evaluate the whatever available records for the last page
                 */
                per_page = (vars.page < vars.total_pages) ? options.per_page : vars.total_records - ((vars.total_pages - 1) * options.per_page)

                ;

                /**
                 * if no any data is available in the global data array, then exit
                 * or if requested page data is not loaded in the global data array. then exit
                 */
                if (rows.length === 0 || rows[idx] === undefined) {
                    Load.PageData(idx, per_page, function () {
                        Get.PageData(page_nr, callback);
                    });
                }
                else {
                    if(rows[0]!==null){
                        for (i = idx, j = 0; j < per_page; i++, j++) {
                            /**
                         * if there is not enought data to load, then break the loop
                         */
                            if (rows[i] === undefined) {
                                break;
                            }

                            data_arr.push(rows[i]);
                        }


                        if (data_arr.length < per_page) {
                            Load.PageData(data_arr.length, options.per_page - data_arr.length, function () {
                                Get.PageData(page_nr, callback);
                            });
                        }
                    }
                    if (typeof callback === 'function') {
                        callback(data_arr);
                    }

                }
            },
            Listing: function (data, tpl) {
                var html = '', tmp, field;
                $(data).each(function (i, rowdata) {
                    if (rowdata === undefined) {
                        return;
                    }

                    tmp = tpl;
                    for (field in rowdata) {
                        if (rowdata.hasOwnProperty(field)) {
                            if (rowdata[field] === null) {
                                rowdata[field] = 'null';
                            }

                            var re = new RegExp('\\$(?:\\{|%7B)' + field + '(?:\\}|%7D)', 'gi');
                            tmp = tmp.replace(re, rowdata[field].toString().replace('$', '$$$'));
                        }
                    }
                    html += tmp;
                });
                return html;
            }
        },

        Parse = {
            Paging: function () {
                var keys = {
                    page_nr: vars.page,
                    total_pages: vars.total_pages
                };
                Parse.Template(keys);
            },

            Template: function (keys) {
                for (var key in keys) {
                    if (keys.hasOwnProperty(key)) {
                        $OBJ.Holder.find('.' + key).html(keys[key]);
                    }
                }
            }
        },

        Show = {
            Page: function (callback) {
                Get.PageData(vars.page, function (data) {
                    var html=Get.Listing(data, options.template.row);

                    if(typeof(options.beforeShow)=='function')
                        html = options.beforeShow(html);

                    if(typeof(html)=='undefined') {
                        console.log('beforeShow did not return proper html');
                        return false;
                    }

                    Animate.Fade($OBJ.Listing, html, function(){
                        if(typeof(options.afterShow)=='function')
                            options.afterShow.apply($OBJ.Listing.get(0),[rows]);
                    });

                    if(typeof(callback)=='function') callback();

                });
            }
        },

        Animate = {
            Fade: function ($el, html, callback) {
                $el.fadeOut(function () {
                    $el.html(html).fadeIn(callback);
                });
            },

            SlideLeft: function ($el, html) {
                $el.animate({
                    left: parseInt($el.css('left'),10) == 0 ? -$el.outerWidth() : 0
                }, function () {
                    if (html !== null) {
                        $el.html(html);
                    //Animate.SlideLeft($el, null);
                    }
                });
            }
        };

        return $(this).each(function () {
            $OBJ.Holder = $(this);
            $OBJ.Holder.html(options.template.layout);
            $OBJ.Listing = $OBJ.Holder.find('.listing');

            /**
             * initialize the assignments
             */
            vars.page = options.initial_page;
            rows = options.data;

            /**
             * load the paging summary
             */
            Load.SummaryData(function () {
                /**
                 * parse the paging section
                 */
                Parse.Paging();

                /**
                 * show the initial page
                 */
                Show.Page(function(){
                    if(typeof(callback)=='function') callback.apply(self,[rows]);
                });
            });

            /**
             * assign the click event for next button
             */
            $OBJ.Holder.find('[class^=btn-]').click(function () {
                var page = vars.page;

                switch (this.className)
                {
                    case 'btn-next':
                        if (page < vars.total_pages) {
                            vars.page++;
                            $OBJ.Holder.trigger('change.page.rorep.nmm',[vars.page]);
                        }
                        break;
                    case 'btn-prev':
                        if (page > 1) {
                            vars.page--;
                            $OBJ.Holder.trigger('change.page.rorep.nmm',[vars.page]);
                        }
                        break;
                }

                if (page !== vars.page) {
                    Parse.Paging();
                    Show.Page(function(){
                        if(typeof(callback)=='function') callback.apply(self,[rows]);
                    });
                }
            });
        });

    };
})(jQuery);